package jbrowser.services;

import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.ServiceLoader;
import java.util.logging.Level;
import java.util.logging.Logger;
import jbrowser.JBrowser;

/**
 *
 * @author buczko
 */
public class DefaultServiceRegistry implements ServiceRegistry {

    private static final Logger logger = Logger.getLogger(DefaultServiceRegistry.class.getName());
    private static final String CONFIG_NAME = "jbrowser.services";
    private static final FileFilter fileFilter = new FileFilter() {
        @Override
        public boolean accept(File file) {
            return file.getName().endsWith(".jar");
        }
    };

    @Override
    public String getName() {
        return "DefaultServiceRegistry";
    }

    private static class SingletonClassloader {

        public static final URLClassLoader instance = new URLClassLoader(getURLs());

        private static URL[] getURLs() {
            logger.log(Level.INFO, "Create singleton classloader. Adding services to classpath.");
            Iterator<ConfigurationService> configs = ServiceLoader.load(ConfigurationService.class).iterator();
            if (configs.hasNext()) {
                ConfigurationService confService = configs.next();
                Properties properties = confService.getProperties(CONFIG_NAME);
                // First boot
                if (properties == null) {
                    logger.log(Level.INFO, "ConfigurationService returns null. Create new config.");
                    properties = new Properties();
                }
                if (properties.getProperty("jbrowser.services.folder") == null) {
                    properties.setProperty("jbrowser.services.folder", "services");
                }
                File servicesFolder = new File(JBrowser.getHome(), properties.getProperty("jbrowser.services.folder"));
                if (!servicesFolder.exists()) {
                    servicesFolder.mkdir();
                }
                //
                try {
                    File[] jarFiles = servicesFolder.listFiles(fileFilter);
                    List<URL> urls = new ArrayList<URL>(jarFiles.length);
                    for (int i = 0; i < jarFiles.length; i++) {
                        String load = properties.getProperty(jarFiles[i].getName());
                        if (load == null) {
                            // New plugin
                            logger.log(Level.INFO, "Found and load new service: {0}", jarFiles[i].getName());
                            properties.setProperty(jarFiles[i].getName(), "true");
                            urls.add(jarFiles[i].toURI().toURL());
                        } else {
                            if (Boolean.parseBoolean(load)) {
                                logger.log(Level.INFO, "Loading service: {0}", jarFiles[i].getName());
                                urls.add(jarFiles[i].toURI().toURL());
                            } else {
                                logger.log(Level.INFO, "Skip service: {0}", jarFiles[i].getName());
                            }
                        }
                    }
                    confService.setProperties(CONFIG_NAME, properties);
                    return urls.toArray(new URL[urls.size()]);
                    //          
                } catch (Exception ex) {
                    logger.log(Level.SEVERE, null, ex);
                    throw new RuntimeException(ex);
                }
            } else {
                logger.log(Level.SEVERE, "ConfigurationService not found!");
                throw new RuntimeException("ConfigurationService not found!");
            }
        }
    }

    @Override
    public <T> List<T> get(Class<T> service) {
        logger.log(Level.INFO, "get({0})", service);
        List<T> collection = new ArrayList<T>();
        Iterator<T> services = ServiceLoader.load(service, SingletonClassloader.instance).iterator();
        while (services.hasNext()) {
            collection.add(services.next());
        }
        return collection;
    }

    public DefaultServiceRegistry() {
        logger.log(Level.INFO, "Create new DefaultServiceRegistry.");
    }
}